Exercise: From Class to Dataclass

Convert the following classes into dataclasses such that the initializers that the dataclass generates have the same behavior as the regular class:

class A:
 def __init__(self) -> None:
    self._length = 0

class B:
 def __init__(self, x: int, y: str = "hello", values: list[int] | None = None) -> None:
    self.x = x
   self.y = y
   self.values = [] if not values else values

class C:
 def __init__(self, a: int = 3) -> None:
   self.a = a
   self.b = a + 3

Compatible Python Versions: 3.9+


Agustin Rodriguez

Hi this is my solution :
@dataclass
class A:
_length: int = field(default=0,init=False)

@dataclass
class B:
x: int
y: str = "Hello"
l: list[int] = field(default_factory=list)

@dataclass
class C:
a: int = 3
b: int = field(init=False)

def __post_init__(self):
self.b = self.a + 3

@dataclass
class Customer:
name: str
address: str
email: str

@dataclass
class Phone:
brand: str
model: str
price: float = field(default=0)
serial_number: str

@dataclass
class Plan:
customer: Customer
phone: Phone
start_date: datetime = field(default_factory=datetime.now)
months: int = 12
monthly_price: float = 0.0
phone_included: bool

REPLY
Andreas [ArjanCodes Team]

Looks good! I am a bit curious about the price: float = field(default=0) part. Does your Linter complain about this? Because the type would be inferred to be an integer, but it is overridden as a float.

REPLY
Agustin Rodriguez

Not really, but I disabled the linter because it was a little annoying in my work. (Sorry for my poor English.

)

REPLY
Andreas [ArjanCodes Team]

I get that! But I highly suggest that you use a linter with strict type annotations. It will be a bit painful in the beginning, but in the end, it will benefit you a lot!

REPLY
Agustin Rodriguez

ok thanks

REPLY
Scott Blake

Exercise 1:
```
from dataclasses import dataclass, field

@dataclass
class A:
_length: int = field(default=0, init=False)

@dataclass
class B:
x: int
y: str = "hello"
l: list[int] = field(default_factory=list)

@dataclass
class C:
a: int = field(default=3)
b: int = field(init=False)

def __post_init__(self) -> None:
self.b = self.a + 3
```

Exercise 2:
```
from dataclasses import dataclass, field
from datetime import datetime
from random import randint

def date_today() -> str:
return datetime.now().strftime("%Y-%m-%d")

def generate_serial() -> str:
return datetime.now().strftime("%Y%m%d") + str(randint(0, 100000))

@dataclass
class Address:
building_number: int
street_name: str
unit_number: int | None
city: str
state: str
zip_code: int
country: str

@dataclass
class Customer:
first_name: str
last_name: str
address: Address
email: str

@dataclass
class Phone:
brand: str
model: str
price: int
serial_number: str = field(init=False, default_factory=generate_serial)

@dataclass
class Plan:
customer: Customer
phone: Phone
start_date: str = field(init=False, default_factory=date_today)
contract_length: int
monthly_price: int
phone_included: bool = False
```

REPLY
Andreas [ArjanCodes Team]

Nice solution Scott! This looks great :)

REPLY
Mithun Banerjee

Exercise 1: Is this correct?
from dataclasses import dataclass, field
from typing import List, Optional

@dataclass
class A:
_length: int = field(default=0, init=False)

@dataclass
class B:
x: int
y: str = "hello"
l: Optional[List[int]] = field(default_factory=list)

@dataclass
class C:
a: int = 3
b: int = field(init=False)

def __post_init__(self):
self.b = self.a + 3

REPLY
Andreas [ArjanCodes Team]

Looks good! Only a minor remark, you can use the built-in list as a type instead of using the typing module. Furthermore, you can use the | operator instead of Optional

REPLY
Prajwal Shetty

Exercise 2:
from dataclasses import dataclass
from datetime import datetime

@dataclass
class Customer:
name: str
address: str
email_address: str

@dataclass
class Phone:
brand: str
model: str
price: float
serial_number: str

@dataclass
class Plan:
customer: Customer
phone: Phone
contract_start_date: datetime
total_contract_months: int
monthly_price: float
phone_included_in_contract: bool

REPLY
Andreas [ArjanCodes Team]

Looks good! Nice work with the exercise!

REPLY
Prajwal Shetty

Exercise 1:

from dataclasses import dataclass, field

@dataclass
class A:
_length: str

@dataclass
class B:
x: int
y: str = "hello"
l: list[int] = field(default_factory=list)

@dataclass
class C:
b: int
a: int = field(default=3)

def __post_init__(self):
self.b = self.a + 3

In Class C, code worked only when I moved the attribude b to the begin or I had to set a default value to b. Which one do you think is a better option?

REPLY
Andreas [ArjanCodes Team]

I would say, follow the convention of Python of having arguments with not defaults first

REPLY
Show More